package com.qwc.blog.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qwc.blog.common.constant.ResponseConstant;
import com.qwc.blog.common.exception.BadRequestException;
import com.qwc.blog.entity.*;
import com.qwc.blog.entity.vo.posts.AddPostDto;
import com.qwc.blog.entity.vo.posts.PostsListReqDto;
import com.qwc.blog.mapper.CategoryMapper;
import com.qwc.blog.mapper.ContentMapper;
import com.qwc.blog.mapper.PostsMapper;
import com.qwc.blog.mapper.TagMapper;
import com.qwc.blog.service.IPostsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static com.qwc.blog.common.constant.CommonConstant.*;

/**
 * <p>
 * 文章元信息表 服务实现类
 * </p>
 *
 * @author qwc
 * @since 2021-11-01
 */
@Service
public class PostsServiceImpl extends ServiceImpl<PostsMapper, Posts> implements IPostsService {

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private TagMapper tagMapper;

    @Autowired
    private ContentMapper contentMapper;

    public static final Long PAGE_ONE = 1L;
    public static final Long NEW_BLOG_NUM = 3L;
    public static final Long HOT_BLOG_NUM = 5L;

    @Override
    public Page<Posts> getPostsList(PostsListReqDto dto) {
        List<Integer> categoryIds = new ArrayList<>();
        getCategoryIds(dto.getCategoryId(), categoryIds);
        Page<Posts> page = new PageDTO<>();
        page.setCurrent(dto.getPageNum());
        page.setSize(dto.getPageSize());
        return baseMapper.selectByCategoryIds(page, dto, categoryIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void savePosts(AddPostDto dto, User currentUser) {
        Posts post = dto.getPost();
        post.setAuthor(currentUser.getNickname());
        post.setUserId(currentUser.getUserId());
        checkSameTitle(post);
        baseMapper.insert(post);
        setPostsTags(post, dto.getTags());
        setPostsCategory(post, transformCategories(dto.getCategories()));
        setPostsContent(post, dto.getContent());
    }

    @Override
    public List<String> getTags(Integer postsId) {
        return baseMapper.getTagsByPosts(postsId);
    }

    @Override
    public List<List<Integer>> getCategories(Integer postsId) {
        List<Category> categories = baseMapper.getCategoriesByPosts(postsId);
        return createListCategory(categories);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePosts(AddPostDto dto, User currentUser) {
        Posts post = dto.getPost();
        post.setStatus(NO);
        checkSameTitle(post);
        baseMapper.updateById(post);
        setPostsTags(post, dto.getTags());
        setPostsCategory(post, transformCategories(dto.getCategories()));
        setPostsContent(post, dto.getContent());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removePostsAndPostsCategory(List<Integer> asList) {
        baseMapper.deleteBatchIds(asList);
        baseMapper.deleteBatchPosts(asList);
        baseMapper.deleteBatchTags(asList);
    }

    @Override
    public List<Posts> getNewBlogList() {
        Page<Posts> postsPage = baseMapper.selectPage(PageDTO.of(PAGE_ONE, NEW_BLOG_NUM, false)
                , Wrappers.<Posts>lambdaQuery()
                        .eq(Posts::getStatus, YES)
                        .orderByDesc(Posts::getCreateTime));
        return postsPage.getRecords();
    }

    @Override
    public List<Posts> getHotBlogList() {
        Page<Posts> postsPage = baseMapper.selectPage(PageDTO.of(PAGE_ONE, HOT_BLOG_NUM, false)
                , Wrappers.<Posts>lambdaQuery()
                        .eq(Posts::getStatus, YES)
                        .orderByDesc(Posts::getViews));
        return postsPage.getRecords();
    }

    private List<List<Integer>> createListCategory(List<Category> categories) {
        List<List<Integer>> listCategory = new ArrayList<>();
        for (Iterator<Category> iterator = categories.iterator(); iterator.hasNext(); ) {
            Category next = iterator.next();
            List<Integer> integers = new ArrayList<>();
            getParentCategory(next, integers);
            if (CollUtil.isNotEmpty(integers)) {
                Collections.reverse(integers);
                listCategory.add(integers);
            }
        }
        return listCategory;
    }

    private void getParentCategory(Category next, List<Integer> integers) {
        integers.add(next.getCategoryId());
        Category parent = categoryMapper.selectOne(Wrappers.<Category>lambdaQuery()
                .eq(Category::getStatus, YES)
                .eq(Category::getCategoryId, next.getPid()));
        if (Objects.nonNull(parent)) {
            getParentCategory(parent, integers);
        }
    }

    private List<Integer> transformCategories(List<List<Integer>> categories) {
        List<Integer> categoryIds = new ArrayList<>();
        for (List<Integer> category : categories) {
            if (CollUtil.isNotEmpty(category)) {
                categoryIds.add(category.get(category.size() - 1));
            }
        }
        return categoryIds;
    }

    private void setPostsContent(Posts post, Content content) {
        contentMapper.delete(Wrappers.<Content>lambdaQuery().eq(Content::getPostsId, post.getPostsId()));
        content.setPostsId(post.getPostsId());
        contentMapper.insert(content);
    }

    private void setPostsCategory(Posts post, List<Integer> categories) {
        baseMapper.deleteCategoryByPost(post.getPostsId());
        if (CollUtil.isNotEmpty(categories) && Objects.nonNull(post.getPostsId())) {
            baseMapper.setCategories(categories, post.getPostsId());
        }
    }

    private void setPostsTags(Posts post, List<String> tags) {
        baseMapper.deleteTagsByPost(post.getPostsId());
        if (CollUtil.isNotEmpty(tags) && Objects.nonNull(post.getPostsId())) {
            List<Tag> tags1 = tagMapper.selectList(Wrappers.<Tag>lambdaQuery().in(Tag::getName, tags));
            List<Tag> collect = tags.stream().filter(tag -> tags1.stream().noneMatch(tags11 -> tags11.getName().equals(tag))).map(tagName -> {
                Tag tag = new Tag();
                tag.setName(tagName);
                tag.setCreateBy(post.getAuthor());
                tagMapper.insert(tag);
                return tag;
            }).collect(Collectors.toList());
            tags1.addAll(collect);
            baseMapper.setTags(tags1.stream().map(Tag::getTagId).collect(Collectors.toList()), post.getPostsId());
        }
    }

    private void checkSameTitle(Posts post) {
        List<Posts> posts = baseMapper.selectList(Wrappers.<Posts>lambdaQuery().eq(Posts::getTitle, post.getTitle()));
        if (CollUtil.isNotEmpty(posts) && posts.stream().noneMatch(posts1 -> posts1.getPostsId().equals(post.getPostsId()))) {
            throw new BadRequestException(ResponseConstant.USED_TITLE);
        }
    }

    private void getCategoryIds(Integer categoryId, List<Integer> categoryIds) {
        if (Objects.nonNull(categoryId)) {
            categoryIds.add(categoryId);
        }
        List<Category> categories = categoryMapper.selectList(Wrappers.<Category>lambdaQuery().eq(Category::getPid, categoryId).eq(Category::getStatus, YES));
        List<Integer> ids = categories.stream().map(Category::getCategoryId).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(ids)) {
            for (Integer id : ids) {
                getCategoryIds(id, categoryIds);
            }
        }
    }
}
